
	title	'RAMDISK - A Memory-Based Virtual Drive'

;	THIS PROGRAM IS HEREBY PLACED IN THE PUBLIC DOMAIN,
;	    AND MAY BE FREELY COPIED AND DISTRIBUTED.

;	program:	RAMDISK
;
;	created by      Context Sensitive Inc.
;	                4200 Aurora Ave. N.
;	                Seattle, WA  98103
;	                (206) 632-0301
;
;	version:	1.1		October 28, 1983
;	author:		Ron Blanford
;
;	The handler code for the RAM disk takes approximately 250h bytes,
;	or just over 1/2 kbyte.  In versions of CP/M-86 which include
;	hard disk support, the hard disk initialization code is only
;	executed once at cold boot, so this program can overlay it with the
;	handler code to save on memory usage.  To enable the overlay
;	logic, terminate the allocation size with a 'K', for example:
;		RAMDISK 128K
;	Otherwise the handler code will be placed at the beginning of
;	the TPA, reducing the available memory by a small amount.
;
;	In the standard CP/M the default interrupt vector does nothing but
;	an IRET and return to the program, effectively making all unplanned
;	interrupts (such as arithmetic exceptions and pressing the keyboard
;	reset button) invisible.  This program causes any such interrupt
;	to perform a warm boot, returning the user to the CP/M prompt.


BIOS_segment	equ	0040h
BIOS_jumptable	equ	2500h

	cseg
	org	100h

	mov	dx,offset T_GETSEGT	;get available memory size for messages
	mov	cl,50
	int	224
	mov	ax,BIOS_segment
	mov	es,ax
	mov	ax,es:3[bx]
	mov	cl,6			;convert from pages to kbytes
	shr	ax,cl
	push	ax
	mov	bx,offset h1_size+2
	call	binasc
	pop	ax
	push	ax
	mov	bx,offset h4_size+2
	call	binasc
	pop	ax
	mov	bx,offset nm_size+2
	call	binasc

	mov	dx,offset company_name	;print company name message
	mov	cl,9
	int	224

	mov	bx,80h			;check command buffer
	mov	cl,[bx]			; if no command tail was entered,
	or	cl,cl			; display help screens
	jnz	RD04
	mov	dx,offset help1
	mov	cl,9
	int	224
	mov	cl,1
	int	224
	cmp	al,ctrl_C
	jnz	$+5
	jmp	exit1
	mov	dx,offset help3
	mov	cl,9
	int	224
	mov	cl,1
	int	224
	cmp	al,ctrl_C
	jnz	$+5
	jmp	exit1
	mov	dx,offset help4
	jmp	exit

RD04:	mov	dx,offset T_SELDSK	;see if drive already exists
	mov	cl,50
	int	224
	or	bx,bx
	jz	RD05
	mov	dx,offset I_exists
	jmp	exit

RD05:	mov	bx,80h			;get allocation size from command buffer
	mov	cl,[bx]
	sub	ch,ch
	sub	ax,ax
RD10:	inc	bx			;convert ascii digits to binary
	cmp	byte ptr [bx],' '
	jbe	RD20
	cmp	byte ptr [bx],'0'
	jb	RD21
	cmp	byte ptr [bx],'9'
	ja	RD21
	mul	ten
	mov	dl,byte ptr [bx]
	sub	dl,'0'
	add	ax,dx
RD20:	loop	RD10
RD21:	or	ax,ax			;make sure size specification is in range
	jnz	RD30
	mov	dx,offset nosize
	jmp	exit

RD30:	mov	CBOOT_overlay,1		;if nnnK entered, then put handlers
	or	byte ptr [bx],20h	; in space occupied by CBOOT code
	cmp	byte ptr [bx],'k'
	jz	RD35
	mov	CBOOT_overlay,0
RD35:	cmp	ax,512			;max RAMDISK size is 512K
	jbe	RD40
	mov	ax,512
RD40:	push	ax			;put allocation in 'created' message
	mov	bx,offset cr_size+2
	call	binasc
	pop	ax
	mov	cl,6
	cmp	ax,256			;if >256K, then change parameters
	jbe	RD45			; to block at 2048
	mov	byte ptr BSH,4		; rather than default 1024
	mov	byte ptr BLM,15
	mov	byte ptr EXM,1
	mov	word ptr DRM,127
	shr	ax,1
	mov	cl,7
RD45:	mov	word ptr DSM,ax
	dec	word ptr DSM
	shl	ax,cl
	mov	disk_pages,ax
RD50:	mov	dx,offset T_GETSEGT	;see if enough memory exists
	mov	cl,50
	int	224
	mov	ax,BIOS_segment
	mov	es,ax
	mov	dx,es:3[bx]		;number of pages available

	cmp	CBOOT_overlay,1
	jz	RD55

	mov	ax,offset end_Kdrive	;this code puts the RAMDISK handlers
	sub	ax,offset init_Kdrive	; at the beginning of the TPA
	mov	cl,4			; rather than overlaying CBOOT code.
	shr	ax,cl
	inc	ax
	sub	dx,ax

RD55:	sub	dx,disk_pages
	ja	RD60
	mov	dx,offset nomem
	jmp	exit

RD60:	mov	es:3[bx],dx		;save new TPA length
	mov	di,es:.BIOS_jumptable+1
	add	di,BIOS_jumptable+3

	cmp	CBOOT_overlay,1
	jz	RD65

	mov	di,es:1[bx]		;this code puts the RAMDISK handlers
	add	es:1[bx],ax		; at the beginning of the TPA
	mov	ax,es			; rather than overlaying CBOOT code.
	sub	di,ax
	mov	cl,4
	shl	di,cl

RD65:	mov	init_addr,di
	add	dx,es:1[bx]
	mov	disk_seg,dx
	mov	bx,BIOS_jumptable+18h	;move BIOS jumptable to local table
	mov	di,offset BIOS_HOME	; after converting from relative
	mov	cx,10			; to absolute addressing
RD70:	inc	bx
	mov	ax,es:[bx]
	inc	bx
	inc	bx
	add	ax,bx
	mov	[di],ax
	inc	di
	inc	di
	loop	RD70

	mov	dx,init_addr		;move my entry points to BIOS jump table
	sub	dx,2
	sub	dx,offset init_Kdrive
	mov	di,BIOS_jumptable+18h
	inc	di
	mov	ax,offset HOME
	add	ax,dx
	sub	ax,di
	mov	es:[di],ax
	add	di,3
	mov	ax,offset SELDSK
	add	ax,dx
	sub	ax,di
	mov	es:[di],ax
	add	di,3
	mov	ax,offset SETTRK
	add	ax,dx
	sub	ax,di
	mov	es:[di],ax
	add	di,3
	mov	ax,offset SETSEC
	add	ax,dx
	sub	ax,di
	mov	es:[di],ax
	add	di,3
	mov	ax,offset SETDMA
	add	ax,dx
	sub	ax,di
	mov	es:[di],ax
	add	di,3
	mov	ax,offset READ
	add	ax,dx
	sub	ax,di
	mov	es:[di],ax
	add	di,3
	mov	ax,offset WRITE
	add	ax,dx
	sub	ax,di
	mov	es:[di],ax
	add	di,6			;skip list status jump
	mov	ax,offset SECTRAN
	add	ax,dx
	sub	ax,di
	mov	es:[di],ax
	add	di,3
	mov	ax,offset SETDMAB
	add	ax,dx
	sub	ax,di
	mov	es:[di],ax

	sub	ax,ax			;set up interrupt handler
	mov	es,ax			; by replacing normal IRET
	mov	di,es:.0		; with JMP to my handler
	mov	ax,es:.2
	mov	es,ax
	mov	al,0E9h			;JMP instruction
	mov	es:[di],al
	inc	di
	mov	ax,offset inttrap
	add	ax,dx
	sub	ax,di
	mov	es:[di],ax

	add	dx,2			;change local addresses to new location
	mov	adj_offset,dx
	add	DPH_offset,dx
	add	DIRBUF_offset,dx
	add	DPB_offset,dx
	add	CSV_offset,dx
	add	ALV_offset,dx

	mov	di,init_addr		;move code and data into BIOS
	mov	si,offset init_Kdrive
	mov	cx,offset end_Kdrive
	sub	cx,si
	shr	cx,1
	inc	cx
	cld
	rep movs ax,ax
	mov	dx,offset created
	mov	cl,9
	int	224
	jmpf	dword ptr init_addr	;go execute disk initialization routine
init_addr	dw	BIOS_jumptable+100h
		dw	BIOS_segment


;	BINASC converts an unsigned binary number to ascii representation.
;		Input registers:	ax = number to convert
;					bx = ptr to memory location
;						 for least significant digit
binasc:
	mov	byte ptr [bx],'0'
	mov	cx,10
ba10:	mov	dx,0			;convert ax to ascii
	div	cx
	or	ax,ax
	jnz	ba15
	or	dx,dx
	jz	ba20
ba15:	or	dl,'0'
	mov	byte ptr [bx],dl	;store ascii digit in display area
	dec	bx
	jmps	ba10
ba20:	ret

exit1:	mov	dx,offset abort_msg
exit:	mov	cl,9
	int	224
	mov	dx,0
	mov	cl,0
	int	224


	eject

ctrl_C		equ	03h
cr		equ	0Dh
lf		equ	0Ah
esc		equ	1Bh

ten		dw	10
CBOOT_overlay	db	1	;1=overlay CBOOT code, 0=use part of TPA

T_SELDSK	db	9	;direct BIOS call for disk selection
		dw	10
		dw	0
T_GETSEGT	db	18	;direct BIOS call for memory extents
		dw	0
		dw	0

company_name	db	esc,'[1;1H',esc,'[J',cr,lf,'       ',esc,'[2;3m ',esc,'[3;23m'
		db	'                           RAMDISK  1.1                         '
		db	esc,'[2m ',esc,'[m',cr,lf,'       ',esc,'[2;4m ',esc,'[4;20m'
		db	'  Context Sensitive Inc.  Seattle, Washington   (206) NEC-0301  '
		db	esc,'[2m '
		db	esc,'[m',cr,lf,'$'
help1		db	cr,lf,esc,'[19m'
		db	cr,lf,'           Thank you for choosing RAMDISK from Context Sensitive Inc.'
		db	cr,lf,esc,'[20m'
		db	cr,lf,'          This program creates  a pseudo disk drive  using part of the'
		db	cr,lf,'          random access memory (RAM)  of your computer.  Your computer'
		db	cr,lf,'          has '
h1_size	db	'   '
		db	' kbytes  of memory  available  for program execution.'
		db	cr,lf,'          Most programs require less than 64 kbytes,  so any remaining'
		db	cr,lf,'          memory would otherwise be unused.'
		db	cr,lf
		db	cr,lf,'          The main advantage  of using a RAM disk  is its speed.   The'
		db	cr,lf,'          main drawback  is that it is  not permanent.   Each time the'
		db	cr,lf,'          computer  is  turned  off  or  CP/M-86  rebooted  using  the'
		db	cr,lf,'          CTRL-FNC-BREAK sequence,   all files stored  on the RAM disk'
		db	cr,lf,'          are deleted and the RAM disk itself is eliminated. Therefore'
		db	cr,lf,'          be sure  to save  all modified files  on a permanent disk at'
		db	cr,lf,'          the end of every session with the computer.  In an emergency'
		db	cr,lf,'          press the reset button behind the rubber stopper in the key-'
		db	cr,lf,'          board to return to CP/M-86 without erasing the RAM disk.'
		db	cr,lf
		db	esc,'[25;1H',esc,'[21m          Press any key to continue...',esc,'[m$'
help3		db	esc,'[7;1H',esc,'[J'
		db	cr,lf,'          The  RAM disk  created  by this program  is called  drive K.'
		db	cr,lf,'          Drive K  is used  the same way  as any other disk drive.  To'
		db	cr,lf,'          list the directory of files, type:'
		db	cr,lf,esc,'[23m                    DIR K:',esc,'[20m'
		db	cr,lf,'          (Initially drive K is empty  and this command  will give the'
		db	cr,lf,'          NO FILE  message.)   To  copy  programs  and  data  files to'
		db	cr,lf,'          drive K, type:'
		db	cr,lf,esc,'[23m                    PIP K:=filename.typ',esc,'[20m'
		db	cr,lf,'          To select drive K as the default drive, just type:'
		db	cr,lf,esc,'[23m                    K:',esc,'[20m'
		db	cr,lf,'          in response to the CP/M-86 prompt.'
		db	cr,lf
		db	esc,'[25;1H',esc,'[21m          Press any key to continue...',esc,'[m$'
help4		db	esc,'[7;1H',esc,'[J'
		db	cr,lf,'          To create the RAM disk, type:'
		db	cr,lf,esc,'[23m                    RAMDISK nnn',esc,'[20m'
		db	cr,lf,'          where  nnn is the number of kbytes of memory to allocate for'
		db	cr,lf,'          drive K.  Remember, this must be less than '
h4_size		db	'   '
		db	' kbytes.'
		db	cr,lf,esc,'[m$'
abort_msg	db	0Bh,esc,'[m',esc,'[J$'
nosize		db	cr,lf,esc,'[19m'
		db	cr,lf,'                  A size must be specified for RAMDISK drive K'
		db	cr,lf,esc,'[21m'
		db	cr,lf,'              for example:  ''RAMDISK 128'' allocates 128K of memory'
		db	esc,'[m',cr,lf,'$'
nomem		db	cr,lf,esc,'[19m'
		db	cr,lf,'                 RAMDISK drive K not created - not enough memory'
		db	cr,lf,esc,'[21m'
		db	cr,lf,'              Your computer only has '
nm_size		db	'   '
		db	' kbytes of memory available'
		db	esc,'[m',cr,lf,'$'
I_exists	db	cr,lf,esc,'[19m'
		db	cr,lf,'                 RAMDISK drive K not created - it already exists'
		db	cr,lf,esc,'[21m'
		db	cr,lf,'             CTRL-FNC-BREAK deletes all files and removes the drive'
		db	esc,'[m',cr,lf,'$'
created		db	cr,lf,esc,'[19m'
		db	cr,lf,'                  RAMDISK drive K has been created ('
cr_size		db	'   '
		db	' kbytes)'
		db	cr,lf,esc,'[21m'
		db	cr,lf,'             CTRL-FNC-BREAK deletes all files and removes the drive'
		db	esc,'[m',cr,lf,'$'


	title	'RAMDISK initialization and handler code'
	eject

init_Kdrive:
	call	adjust
	mov	es,disk_seg[bx]
	mov	ax,disk_pages[bx]
init1:	push	ax			;format the disk area
	mov	cx,8000h
	cmp	ax,1000h
	ja	init2
	mov	cl,3
	shl	ax,cl
	mov	cx,ax
init2:	mov	di,0
	mov	ax,0E5E5h
	cld
	rep stos ax
	mov	ax,es
	add	ax,1000h
	mov	es,ax
	pop	ax
	sub	ax,1000h
	ja	init1
	mov	dx,0
	mov	cl,0
	int	224

HOME:	call	adjust
	cmp	byte ptr sek_dsk[bx],10
	jz	$+7
	jmp	BIOS_HOME[bx]
	ret

SELDSK:	call	adjust
	mov	byte ptr sek_dsk[bx],cl
	cmp	cl,10
	jz	$+7
	jmp	BIOS_SELDSK[bx]
	mov	bx,DPH_offset[bx]
	ret

SETTRK:	call	adjust
	cmp	byte ptr sek_dsk[bx],10
	jz	$+7
	jmp	BIOS_SETTRK[bx]
	mov	sek_trk[bx],cx
	ret

SETSEC:	call	adjust
	cmp	byte ptr sek_dsk[bx],10
	jz	$+7
	jmp	BIOS_SETSEC[bx]
	mov	sek_sec[bx],cl
	ret

SETDMA:	call	adjust
	mov	DMA_offset[bx],cx
	jmp	BIOS_SETDMA[bx]

READ:	call	adjust
	cmp	byte ptr sek_dsk[bx],10
	jz	$+7
	jmp	BIOS_READ[bx]
	pushf
	push	ds
	push	es
	mov	es,DMA_seg[bx]
	mov	di,DMA_offset[bx]
	mov	ax,sek_trk[bx]
	mov	cl,6
	shl	ax,cl
	add	al,sek_sec[bx]
	mul	one_twenty_eight[bx]
	mov	cl,12
	shl	dx,cl
	add	dx,disk_seg[bx]
	mov	ds,dx
	mov	si,ax
	mov	cx,64
	cld
	rep movs ax,ax
	pop	es
	pop	ds
	popf
	sub	ax,ax
	ret

WRITE:	call	adjust
	cmp	byte ptr sek_dsk[bx],10
	jz	$+7
	jmp	BIOS_WRITE[bx]
	pushf
	push	ds
	push	es
	mov	ax,sek_trk[bx]
	mov	cl,6
	shl	ax,cl
	add	al,sek_sec[bx]
	mul	one_twenty_eight[bx]
	mov	cl,12
	shl	dx,cl
	add	dx,disk_seg[bx]
	mov	es,dx
	mov	di,ax
	mov	si,DMA_offset[bx]
	mov	ds,DMA_seg[bx]
	mov	cx,64
	cld
	rep movs ax,ax
	pop	es
	pop	ds
	popf
	sub	ax,ax
	ret

SECTRAN: call	adjust
	cmp	byte ptr sek_dsk[bx],10
	jz	$+7
	jmp	BIOS_SECTRAN[bx]
	mov	bx,cx
	ret

SETDMAB: call	adjust
	mov	DMA_seg[bx],cx
	jmp	BIOS_SETDMAB[bx]

inttrap:
	cli
	pop	ax			;pop old IP, CS, and flags
	pop	ax
	pop	ax
	or	ax,0200h		;enable interrupts on return
	push	ax
	mov	ax,BIOS_segment		;return to WBOOT
	push	ax
	mov	ax,BIOS_jumptable+3
	push	ax
	iret

adjust:		nop			;all variables to be offset
		db	0BBh		; by the amount the program moved
adj_offset	dw	0		;this is a MOV BX,nnnn instruction
		ret


	eject

BIOS_HOME	DW	0
BIOS_SELDSK	DW	0
BIOS_SETTRK	DW	0
BIOS_SETSEC	DW	0
BIOS_SETDMA	DW	0
BIOS_READ	DW	0
BIOS_WRITE	DW	0
BIOS_LISTST	DW	0
BIOS_SECTRAN	DW	0
BIOS_SETDMAB	DW	0

sek_dsk		db	0
sek_sec		db	0
sek_trk		dw	0
DMA_offset	dw	0
DMA_seg		dw	0
disk_pages	dw	0
disk_seg	dw	0

one_twenty_eight dw	128

DPH_offset	dw	offset DPH

DPH		dw	0	;no sector translation
		dw	0
		dw	0
		dw	0
DIRBUF_offset	dw	offset DIRBUF
DPB_offset	dw	offset DPB
CSV_offset	dw	offset CSV
ALV_offset	dw	offset ALV

DIRBUF		rs	128
CSV		rs	0
ALV		rs	32

				;disk size:   <256K        >256K
DPB		dw	64	;SPT
BSH		db	3	;BSH             3            4
BLM		db	7	;BLM             7           15
EXM		db	0	;EXM             0            1
DSM		dw	0	;DSM           #K-1      (#K/2)-1
DRM		dw	63	;DRM            63          127
		db	0C0h	;AL0
		db	0	;AL1
		dw	0	;CKS
		dw	0	;OFF

end_Kdrive	db	0

	end
